//
//  NetworkTools.swift
//  SinaMicroBlog
//
//  Created by wxy on 15/9/3.
//  Copyright © 2015年 wxy. All rights reserved.
//

import UIKit
import AFNetworking
import ReactiveCocoa

enum RequsetMethod: String {
    
    case GET = "GET"
    case POST = "POST"
}

class NetworkTools: AFHTTPSessionManager {
    
    //MARK: - 发送请求时所需App信息
    private let clientId = "1321277934"
    private let appSecret = "fc4f4a5ba1be238a4fc05142ca171dae"
    let redirectUri = "http://www.baidu.com"
    
    //单例类
    static let sharedNetworkTools: NetworkTools = {
        
        //指定baseURL
        var _instance = NetworkTools(baseURL: nil)
        //设置反序列化的支持格式
        _instance.responseSerializer.acceptableContentTypes?.insert("text/plain")
        
        return _instance
    }()
    
    // MARK: - OAuth
    /// - see: [http://open.weibo.com/wiki/Oauth2/authorize](http://open.weibo.com/wiki/Oauth2/authorize)
    var OAuthURL: NSURL {
        
        let urlStr = "https://api.weibo.com/oauth2/authorize?client_id=\(clientId)&redirect_uri=\(redirectUri)"
        
        return NSURL(string: urlStr)!
    }
    
    // MARK: - 发布微博
    /// 发布微博（POST）
    ///
    /// - parameter status: 微博文本 不能超过140个字 需要百分号转译（AFN自动做）
    /// - parameter image:  如果有，就上传图片
    ///
    /// - returns: RACSignal
    /// - see: [http://open.weibo.com/wiki/2/statuses/update](http://open.weibo.com/wiki/2/statuses/update)
    /// - see: [http://open.weibo.com/wiki/2/statuses/upload](http://open.weibo.com/wiki/2/statuses/upload)
    func sendStatus(status: String, image: UIImage?) -> RACSignal {
        
        let params = ["status": status]
        
        // 如果没有图片
        if image == nil {
            // 文本微博
            return request(.POST, urlStr: "https://api.weibo.com/2/statuses/update.json", parameters: params)
        } else {
            // 图片微博
            return upload("https://upload.api.weibo.com/2/statuses/upload.json", parameters: params, image: image!)
        }
        
    }
    
    // MARK: - 微博数据
    /// 加载微博数据（GET）
    /// - parameter since_id: 若指定此参数，则返回ID比since_id大的微博（即比since_id时间晚的微博），默认为0。
    /// - parameter max_id: 若指定此参数，则返回ID小于或等于max_id的微博，默认为0。
    ///
    /// - see: [http://open.weibo.com/wiki/2/statuses/home_timeline](http://open.weibo.com/wiki/2/statuses/home_timeline)
    /// 技巧：第一个参数可以设置外部参数 方便函数调用
    func loadStatuses(since_id since_id: Int, max_id: Int) -> RACSignal {
        
        let urlStr = "https://api.weibo.com/2/statuses/home_timeline.json"
        
        // 创建空的参数字典
        var params = [String: AnyObject]()
        if since_id > 0 {
            
            params["since_id"] = since_id
        }else if max_id > 0 {
            
            // = max_id 的时候会出现重复微博的错误
            params["max_id"] = max_id - 1
        }
        
        return request(.GET, urlStr: urlStr, parameters: params)
    }
    
    /// 获取AcessToken
    ///
    /// - parameter code: 请求码/授权码
    /// - see: [http://open.weibo.com/wiki/OAuth2/access_token](http://open.weibo.com/wiki/OAuth2/access_token)
    func loadAccessToken(code: String) -> RACSignal {
        
        let urlStr = "https://api.weibo.com/oauth2/access_token"
        
        let params = ["client_id":clientId,
            "client_secret": appSecret,
            "grant_type": "authorization_code",
            "code": code,
            "redirect_uri": redirectUri
        ]
        
        return request(.POST, urlStr: urlStr, parameters: params, withToken: false)
    }
    
    // 加载用户信息
    /// - see: [http://open.weibo.com/wiki/2/users/show](http://open.weibo.com/wiki/2/users/show)
    func loadUserInfo(uid: String) -> RACSignal {
        
        let urlStr = "https://api.weibo.com/2/users/show.json"
        
        let params = ["uid": uid]
        
        return request(.GET, urlStr: urlStr, parameters: params)
    }
    
    // MARK: - 私有方法 封装AFN的网络请求
    /// 在指定参数字典中追加 accessToken
    ///
    /// - parameter parameters: parameters 地址
    ///
    /// - returns: 是否成功 如果token失效 返回false
    private func appendToken(inout parameters: [String: AnyObject]?) -> Bool {
        
        // 判断单例中的 token 是否有效
        guard let token = UserAccountViewModel.sharedUserAccount.accessToken else {
            return false
        }
        
        // 判断是否传递了参数字典
        if parameters == nil {
            parameters = [String: AnyObject]()
        }
        
        // 后续的token都是有值的
        parameters!["access_token"] = token
        
        return true
    }
    
    /// 网络请求方法（对AFN的GET & POST 进行封装）
    ///
    /// - parameter method:     请求方法
    /// - parameter urlStr:     请求地址
    /// - parameter parameters: 参数字典
    /// - parameter withToken:  是否包含accessToken，默认带Token访问
    ///
    /// - returns: RACSignal
    private func request(method: RequsetMethod,urlStr: String,var parameters: [String: AnyObject]?, withToken: Bool = true)  -> RACSignal {
        
        return RACSignal.createSignal({ (subscriber) -> RACDisposable! in
            
            // 0. 判断是否需要token 
            // 如果 token 失效 直接返回错误
            if withToken && !self.appendToken(&parameters){
                
                subscriber.sendError(NSError(domain: "com.wing.error", code: -250, userInfo:
                    ["errorMessage":"Token为空"]))
                return nil
            }
    
            //1. 成功的回调闭包
            let successCallBack = { (task: NSURLSessionDataTask, result: AnyObject) -> Void in
                
                // 将结果发送给订阅者
                subscriber.sendNext(result)
                // 完成
                subscriber.sendCompleted()
            }
            
            //2. 失败的回调闭包
            let failureCallBack = { (task: NSURLSessionDataTask, error: NSError) -> Void in
        
                printLog(error, logError: true)
                subscriber.sendError(error)
            }
            
            //3. 根据方法，选择调用不同的网络方法
            if method == .GET {
                
                self.GET(urlStr, parameters: parameters, success: successCallBack, failure: failureCallBack)
            }else{
                self.POST(urlStr, parameters: parameters, success: successCallBack, failure: failureCallBack)
            }
            
            return nil
            
        })
    }
    
    // 上传文件
    private func upload(urlStr: String, var parameters: [String: AnyObject]?, image: UIImage) -> RACSignal {
        
        // 闭包返回值是对信号销毁时需要做的内存销毁工作 同样是一个 block AFN 的可以直接 nil
        return RACSignal.createSignal() { (subscriber) -> RACDisposable! in
            
            // 0. 判断是否需要token
            // 如果 token 失效 直接返回错误
            if !self.appendToken(&parameters){
                
                subscriber.sendError(NSError(domain: "com.wing.error", code: -250, userInfo:
                    ["errorMessage":"Token为空"]))
                return nil
            }
            
            // 1. 调用 AFN 的上传文件方法
            self.POST(urlStr, parameters: parameters, constructingBodyWithBlock: { (formData) -> Void in
                
                // 将图像转换成二进制数据
                let data = UIImagePNGRepresentation(image)!
                // formData 是遵守协议的对象 AFN 内部提供的 使用的时候只需要按照协议方法传递参数即可
                /**
                1. 要上传图片的二进制数据
                2. 服务器的字段名，开发的时候，咨询后台
                3. 保存在服务器的文件名，很多后台允许随便写
                4. mimeType -> 客户端告诉服务器上传文件的类型，格式
                大类/小类
                image/jpg
                image/gif
                image/png
                如果，不想告诉服务器具体的类型，可以使用 application/octet-stream
                */
                
                formData.appendPartWithFileData(data, name: "pic", fileName: "oh", mimeType: "application/octet-stream")
                }, success: { (_, result) -> Void in
                    
                    subscriber.sendNext(result)
                    subscriber.sendCompleted()
                    
                }, failure: { (_, error) -> Void in
                    
                    printLog(error, logError: true)
                    subscriber.sendError(error)
                    
            })
            
            return nil
        }
    }
}


